Dosen : Prof. Dr. Suhartono M.Kom

instansi : UIN Maliki Malang

if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("EBImage")
## Bioconductor version 3.12 (BiocManager 1.30.10), R 4.0.3 (2020-10-10)
## Installing package(s) 'EBImage'
## package 'EBImage' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'EBImage'
## Warning in file.copy(savedcopy, lib, recursive = TRUE):
## problem copying C:\Users\bambang\OneDrive\Dokumen\R\win-
## library\4.0\00LOCK\EBImage\libs\x64\EBImage.dll to C:
## \Users\bambang\OneDrive\Dokumen\R\win-library\4.0\EBImage\libs\x64\EBImage.dll:
## Permission denied
## Warning: restored 'EBImage'
## 
## The downloaded binary packages are in
##  C:\Users\bambang\AppData\Local\Temp\RtmpspUG5V\downloaded_packages
## Installation path not writeable, unable to update packages: boot, class,
##   cluster, codetools, foreign, KernSmooth, MASS, Matrix, mgcv, nlme, nnet,
##   spatial, survival
## Old packages: 'RCurl', 'tiff', 'xfun'
library(EBImage)
# install.packages("jpeg")
library(jpeg)

1 Membaca, menampilkan dan menulis gambar

Fungsionalitas Basic R package (“EBImage”) mencakup membaca, menulis, dan menampilkan gambar. Gambar dibaca menggunakan fungsi readImageyang menerima input nama file atau URL. Untuk memulai, mari kita muat gambar sampel yang didistribusikan dengan paket.

f = system.file("images", "sample.png", package="EBImage")
img = readImage(f)

Paket R ( “EBImage”) saat ini mendukung tiga format file gambar: jpeg, pngdan tiff. Format gambar tambahan dapat diimpor menggunakan R GitHubPkg (“aoles / RBioFormats”), yang menambahkan dukungan untuk berbagai format file termasuk data gambar mikroskopis dan metadata.

Gambar yang diimpor dapat divisualisasikan oleh fungsinya display().

display(img, method="browser")

Dalam sesi R interaktif, displaymembuka gambar di penampil JavaScript di tab browser web. Pintasan mouse atau keyboard memungkinkan memperbesar dan memperkecil gambar, menggeser, dan berputar melalui beberapa bingkai gambar. Gambar juga dapat ditampilkan menggunakan metode plotting R inti, yang memungkinkan untuk menggabungkan gambar dengan fungsionalitas plot lainnya, misalnya, menambahkan label teks di atas gambar.

display(img, method="raster")
text(x = 20, y = 20, label = "Colorful Parrots", adj = c(0,1), col = "orange", cex = 2)

Grafik yang ditampilkan dalam perangkat R dapat disimpan menggunakan fungsi paket R (“basis”) R dev.print atau dev.copy. Misalnya, mari simpan gambar beranotasi kami sebagai file JPEG dan verifikasi ukurannya pada disk.

filename = "parrots.jpg"
dev.print(jpeg, filename = filename , width = dim(img)[1], height = dim(img)[2])
## png 
##   2
file.info(filename)$size
## [1] 6771

Perilaku default displaydapat diubah secara global dengan menyetel “options(”EBImage.display“) to either”browser" or“raster” ". Ini berguna, misalnya, untuk melihat gambar di dalam RStudio. Dimungkinkan juga untuk membaca dan melihat gambar berwarna,

imgcol = readImage(system.file("images", "sample-color.png", package="EBImage"))
display(imgcol)

Gambar mungkin berisi banyak bingkai, dalam hal ini gambar dapat ditampilkan sekaligus dalam pengaturan kisi dengan menetapkan argumen fungsi all = TRUE.

nuc = readImage(system.file("images", "nuclei.tif", package="EBImage"))
display(nuc, method = "raster", all = TRUE)

Selain mengimpor gambar, gambar dapat diekspor (disimpan) ke file menggunakan EBImage::writeImage(). Gambar yang kami muat adalah file PNG. Untuk menyimpan gambar ini sebagai file JPEG, format JPEG memungkinkan untuk mengatur nilai kualitas antara 1 dan 100 untuk mencerminkan tingkat kompresi gambar yang diinginkan. Nilai default dari qualityargumen writeImageadalah 100. Nilai yang lebih kecil menghasilkan gambar dengan ukuran lebih kecil, tetapi resolusinya lebih buruk (kurang detail).

writeImage(imgcol, "sample.jpeg", quality = 85)

Demikian pula, kita bisa menyimpan gambar sebagai file TIFF dan mengatur algoritma kompresi mana yang ingin kita gunakan. Untuk daftar lengkap parameter yang tersedia, lihat ? writeImage

2 Representasi data gambar

EBImage menggunakan kelas khusus paket Imageuntuk menyimpan dan memproses gambar. Ini memperluas kelas dasar R array, dan semua fungsi paket R (“EBImage”) juga bisa dipanggil langsung pada matriks dan array. Anda dapat mengetahui lebih lanjut tentang kelas ini dengan mengetik ?Image. Mari kita intip ke dalam struktur internal suatu Imageobjek.

str(img)
## Formal class 'Image' [package "EBImage"] with 2 slots
##   ..@ .Data    : num [1:768, 1:512] 0.447 0.451 0.463 0.455 0.463 ...
##   ..@ colormode: int 0

..DataSlot berisi array numerik intensitas pixel. Kita melihat bahwa dalam kasus ini arraynya adalah dua dimensi, dengan 768 kali 512 elemen, dan sesuai dengan lebar dan tinggi piksel gambar. Dimensi ini dapat diakses menggunakan dimfungsi, seperti array biasa.

dim(img)
## [1] 768 512

Data gambar dapat diakses sebagai R biasa arraymenggunakan pengakses imageData.

imageData(img)[1:3, 1:6]
##           [,1]      [,2]      [,3]      [,4]      [,5]      [,6]
## [1,] 0.4470588 0.4627451 0.4784314 0.4980392 0.5137255 0.5294118
## [2,] 0.4509804 0.4627451 0.4784314 0.4823529 0.5058824 0.5215686
## [3,] 0.4627451 0.4666667 0.4823529 0.4980392 0.5137255 0.5137255

The as.array()metode dapat digunakan untuk memaksa sebuah Imageke array.

is.Image( as.array(img) )
## [1] FALSE

Distribusi intensitas piksel dapat diplot dalam histogram, dan jangkauannya diperiksa menggunakan rangefungsi tersebut.

hist(img)

range(img)
## [1] 0 1

Ringkasan Imageobjek yang berguna juga disediakan oleh showmetode ini, yang dipanggil jika kita hanya mengetikkan nama objek.

img
## Image 
##   colorMode    : Grayscale 
##   storage.mode : double 
##   dim          : 768 512 
##   frames.total : 1 
##   frames.render: 1 
## 
## imageData(object)[1:5,1:6]
##           [,1]      [,2]      [,3]      [,4]      [,5]      [,6]
## [1,] 0.4470588 0.4627451 0.4784314 0.4980392 0.5137255 0.5294118
## [2,] 0.4509804 0.4627451 0.4784314 0.4823529 0.5058824 0.5215686
## [3,] 0.4627451 0.4666667 0.4823529 0.4980392 0.5137255 0.5137255
## [4,] 0.4549020 0.4666667 0.4862745 0.4980392 0.5176471 0.5411765
## [5,] 0.4627451 0.4627451 0.4823529 0.4980392 0.5137255 0.5411765

Untuk representasi yang lebih ringkas tanpa pratinjau larik intensitas, gunakan print method dengan argumen short disetel ke

print( img, short=TRUE)
## Image 
##   colorMode    : Grayscale 
##   storage.mode : double 
##   dim          : 768 512 
##   frames.total : 1 
##   frames.render: 1

Gambar berwarna didasarkan pada 3 saluran (RBG).

print(imgcol, short=TRUE)
## Image 
##   colorMode    : Color 
##   storage.mode : double 
##   dim          : 768 512 3 
##   frames.total : 3 
##   frames.render: 1

Mereka berbeda dari gambar skala abu-abu berdasarkan properti colorModedan jumlah dimensinya, 3 (B&W) vs. 4 (warna). The colorModeSlot ternyata nyaman ketika berhadapan dengan tumpukan gambar. Jika diatur ke gray-scale, maka dimensi ketiga dan semua yang lebih tinggi dari larik dianggap sebagai bingkai gambar terpisah yang sesuai, misalnya, ke posisi z yang berbeda, titik waktu, ulangan, dll. Di sisi lain, jika colorModeada Color, maka dimensi ketiga diasumsikan memiliki saluran warna yang berbeda, dan hanya dimensi keempat dan lebih tinggi yang digunakan untuk beberapa bingkai gambar. imgcolberisi tiga saluran warna, yang sesuai dengan intensitas merah, hijau, dan biru pada foto. Namun, hal ini tidak harus demikian, dan jumlah saluran warna dapat berubah-ubah.

Bidang “frames.total” dan “frames.render” yang ditampilkan oleh ringkasan objek sesuai dengan jumlah total bingkai yang terdapat dalam gambar, dan dengan jumlah bingkai yang dirender. Angka-angka ini dapat diakses menggunakan fungsi numberOfFramesdengan menentukan typeargumennya.

numberOfFrames(imgcol, type = "render")
## [1] 1
numberOfFrames(imgcol, type = "total")
## [1] 3

Bingkai gambar dapat diekstraksi menggunakan getFramedan getFrames. getFramemengembalikan bingkai ke-i yang terdapat dalam gambar y. Jika typeadalah “total”, fungsi ini tidak menyadari mode warna dan mengembalikan sebuah-bidang xy. Untuk type=“render”fungsi mengembalikan gambar ke-i seperti yang ditunjukkan oleh fungsi tampilan. Sementara getFramemengembalikan hanya satu bingkai, getFramesmengambil daftar bingkai yang dapat berfungsi sebagai input ke lapplyfungsi -family. Lihat bagian “Thresholding global” untuk ilustrasi dari pendekatan ini.

Mari kita lihat data pencitraan nuklir / seluler, yang berisi total 4 frame yang sesuai dengan 4 gambar skala abu-abu yang terpisah, seperti yang ditunjukkan oleh “frames.render”.

nuc
## Image 
##   colorMode    : Grayscale 
##   storage.mode : double 
##   dim          : 510 510 4 
##   frames.total : 4 
##   frames.render: 4 
## 
## imageData(object)[1:5,1:6,1]
##            [,1]       [,2]       [,3]       [,4]       [,5]       [,6]
## [1,] 0.06274510 0.07450980 0.07058824 0.08235294 0.10588235 0.09803922
## [2,] 0.06274510 0.05882353 0.07843137 0.09019608 0.09019608 0.10588235
## [3,] 0.06666667 0.06666667 0.08235294 0.07843137 0.09411765 0.09411765
## [4,] 0.06666667 0.06666667 0.07058824 0.08627451 0.08627451 0.09803922
## [5,] 0.05882353 0.06666667 0.07058824 0.08235294 0.09411765 0.10588235

3 Menajemen Warna

Seperti yang dijelaskan di bagian sebelumnya, kelas Imagememperluas kelas dasar arraydan digunakan colorModeuntuk menyimpan bagaimana informasi warna dari data multi-dimensi harus ditangani.

Fungsi colorModeini dapat digunakan untuk mengakses dan mengubah properti ini, mengubah mode rendering gambar. Misalnya, jika kita mengambil Colorgambar dan mengubah modenya menjadi gray-scale, maka gambar tidak akan ditampilkan sebagai gambar berwarna lagi, melainkan sebagai tiga bingkai skala abu-abu terpisah yang sesuai dengan saluran merah, hijau, dan biru. Fungsi colorModeini tidak mengubah konten gambar yang sebenarnya tetapi hanya mengubah cara gambar ditampilkan oleh paket R (“EBImage”).

colorMode(imgcol) = Grayscale
display(imgcol, all=TRUE)

Konversi ruang warna antara gray-scaledan Colorgambar dilakukan dengan menggunakan fungsi ini channel. Ini memiliki antarmuka yang fleksibel yang memungkinkan untuk mengkonversi salah satu cara antara mode, dan dapat digunakan untuk mengekstrak saluran warna. Tidak seperti colorMode, channelmengubah nilai intensitas piksel gambar.

Colorke gray-scalemode konversi termasuk mengambil rata-rata yang seragam di seluruh saluran RGB, dan mode konversi pelestarian pendaran berbobot yang lebih cocok untuk tujuan tampilan.

The asred, asgreen dan asbluemode mengkonversi gambar skala abu-abu atau array menjadi gambar warna rona tertentu.

Fungsi kenyamanan toRGBmempromosikan gambar skala abu-abu ke ruang warna RGB dengan mereplikasinya di seluruh saluran merah, hijau, dan biru, yang setara dengan panggilan channeldengan mode disetel ke rgb. Saat ditampilkan, gambar ini tidak terlihat berbeda dari skala abu-abunya, yang diharapkan karena informasi antara saluran warna adalah sama. Untuk menggabungkan tiga gambar skala abu-abu menjadi satu gambar rgb, gunakan fungsi tersebut rgbImage.

Fungsi Imageini dapat digunakan untuk membuat gambar berwarna dari vektor karakter atau larik warna R bernama (seperti yang dicantumkan oleh colors()) dan / atau string heksadesimal dalam bentuk “#rrggbb” atau “#rrggbbaa”.

4 Memanipulasi gambar

Menjadi array numerik, gambar dapat dengan mudah dimanipulasi oleh salah satu operator aritmatika R. Misalnya, kita dapat menghasilkan gambar negatif hanya dengan mengurangkan gambar dari nilai maksimumnya.

img_neg = max(img) - img
display( img_neg )

Kami juga dapat meningkatkan kecerahan gambar melalui penambahan, menyesuaikan kontras melalui perkalian, dan menerapkan koreksi gamma melalui eksponen.

img_comb = combine(
  img,
  img + 0.3,
  img * 2,
  img ^ 0.5
)

display(img_comb, all=TRUE)

Dalam contoh di atas, kami telah combinemenggabungkan gambar individual menjadi satu objek gambar multi-bingkai.

Selanjutnya, kami dapat memotong dan membuat ambang gambar dengan operasi matriks standar.

img_crop = img[366:749, 58:441]
img_thresh = img_crop > .5
display(img_thresh)

Operasi thresholding mengembalikan Imageobjek dengan nilai piksel binarisasi. Tipe data R yang digunakan untuk menyimpan gambar seperti itu adalah logical.

img_thresh
## Image 
##   colorMode    : Grayscale 
##   storage.mode : logical 
##   dim          : 384 384 
##   frames.total : 1 
##   frames.render: 1 
## 
## imageData(object)[1:5,1:6]
##       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]
## [1,] FALSE FALSE FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE FALSE FALSE
## [3,] FALSE FALSE FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE FALSE FALSE FALSE FALSE
## [5,] FALSE FALSE FALSE FALSE FALSE FALSE

Untuk transposisi gambar, gunakan transposedaripada fungsi paket R (“basis”) t. Ini karena yang pertama juga berfungsi pada gambar berwarna dan multiframe dengan menukar dimensi spasialnya.

img_t = transpose(img)
display( img_t )

5 Transformasi spasial

Kita hanya melihat satu jenis transformasi spasial, transposisi, tetapi masih banyak lagi, misalnya translasi, rotasi, refleksi, dan penskalaan. translatememindahkan bidang gambar dengan vektor dua dimensi yang ditentukan sedemikian rupa sehingga piksel yang berada di luar wilayah gambar dipotong, dan piksel yang masuk ke wilayah gambar disetel ke latar belakang.

Warna latar belakang dapat disetel menggunakan argumen bg.columum untuk semua fungsi transformasi spasial yang relevan. Default mengatur nilai piksel latar belakang ke nol yang sesuai dengan hitam. Mari kita tunjukkan penggunaan argumen ini rotateyang memutar gambar searah jarum jam dengan sudut yang diberikan.

img_rotate = rotate(img, 30, bg.col = "white")
display(img_rotate)

Untuk menskalakan gambar ke dimensi yang diinginkan, gunakan resize. Jika Anda hanya memberikan salah satu dari lebar atau tinggi, dimensi lainnya secara otomatis dihitung dengan tetap menggunakan rasio aspek asli.

img_resize = resize(img, w=256, h=256)
display(img_resize )

Berfungsi flipdan flopmerefleksikan gambar di sekitar sumbu horizontal dan vertikal, masing-masing.

img_flip = flip(img)
img_flop = flop(img)

display(combine(img_flip, img_flop), all=TRUE)

Transformasi linier spasial diimplementasikan menggunakan affinetransformasi umum . Ini peta koordinat pixel gambar pxmenggunakan matriks transformasi 3x2 mdengan cara berikut: cbind(px, 1) %*% m. Misalnya, pemetaan tipis horizontal dapat diterapkan dengan

m =  matrix(c(1, -.5, 128, 0, 1, 0), nrow=3, ncol=2)
img_affine = affine(img, m)
display( img_affine )

6 Filtering

6.1 Filter Linier

Langkah pra-pemrosesan yang umum melibatkan pembersihan gambar dengan menghilangkan artefak lokal atau noise melalui penghalusan. Pendekatan intuitif adalah menentukan jendela dengan ukuran yang dipilih di sekitar setiap piksel dan rata-rata nilai dalam lingkungan itu. Setelah menerapkan prosedur ini ke semua piksel, gambar baru yang dihaluskan diperoleh. Secara matematis.

Dalam r Biocpkg (“EBImage”), konvolusi 2 dimensi diimplementasikan oleh fungsi filter2, dan fungsi tambahan makeBrushdapat digunakan untuk menghasilkan fungsi bobot. Bahkan, filter2tidak langsung melakukan penjumlahan yang ditunjukkan pada persamaan di atas. Sebaliknya, ia menggunakan Transformasi Fast Fourier dengan cara yang secara matematis setara tetapi secara komputasi lebih efisien.

w = makeBrush(size = 31, shape = 'gaussian', sigma = 5)
plot(w[(nrow(w)+1)/2, ], ylab = "w", xlab = "", cex = 0.7)

img_flo = filter2(img, w)
display(img_flo)

Di sini kita telah menggunakan filter Gaussian dengan lebar 5 yang diberikan oleh sigma. Bentuk filter lain yang tersedia termasuk “box”(default) “disc”,, “diamond”dan “line”, untuk beberapa di antaranya kernel dapat berupa biner; lihat makeBrush untuk detailnya.

Jika gambar yang difilter berisi beberapa bingkai, filter diterapkan ke setiap bingkai secara terpisah. Untuk kenyamanan, gambar juga dapat dihaluskan menggunakan fungsi pembungkus gbluryang melakukan penghalusan Gaussian dengan ukuran filter yang secara otomatis disesuaikan sigma.

nuc_gblur = gblur(nuc, sigma = 5)
display(nuc_gblur, all=TRUE )

Dalam pemrosesan sinyal, operasi penghalusan gambar disebut sebagai pemfilteran low-pass. High-pass filtering adalah operasi kebalikannya yang memungkinkan untuk mendeteksi tepi dan mempertajam gambar. Ini dapat dilakukan, misalnya, menggunakan filter Laplacian.

fhi = matrix(1, nrow = 3, ncol = 3)
fhi[2, 2] = -8
img_fhi = filter2(img, fhi)
display(img_fhi)

6.2 Filter Median

Pendekatan lain untuk melakukan pengurangan kebisingan adalah dengan menerapkan filter median, yang merupakan teknik non-linier sebagai lawan dari filter konvolusi lintasan rendah yang dijelaskan di bagian sebelumnya. Pemfilteran median sangat efektif dalam kasus derau spekel, dan memiliki keuntungan menghilangkan derau sekaligus mempertahankan tepian.

Filter median lokal bekerja dengan memindai gambar piksel demi piksel, mengganti setiap piksel dengan median tetangganya di dalam jendela dengan ukuran yang ditentukan. Teknik pemfilteran ini disediakan dalam paket R (“EBImage”) oleh fungsinya medianFilter. Kami mendemonstrasikan penggunaannya dengan terlebih dahulu merusak gambar dengan noise seragam, dan merekonstruksi gambar asli dengan median filtering.

l = length(img)
n = l/10
pixels = sample(l, n)
img_noisy = img
img_noisy[pixels] = runif(n, min=0, max=1)
display(img_noisy)

img_median = medianFilter(img_noisy, 1)
display(img_median)

6.3 Operasi morfologi

Gambar biner adalah gambar yang hanya berisi dua set piksel, dengan nilai, katakanlah 0 dan 1, mewakili piksel latar belakang dan latar depan. Citra tersebut tunduk pada beberapa operasi morfologi non-linier: erosi, dilatasi, pembukaan, dan penutupan. Operasi ini bekerja dengan melapisi topeng, yang disebut elemen penataan, di atas gambar biner dengan cara berikut:

erosi: Untuk setiap piksel latar depan, letakkan topeng di sekitarnya, dan jika ada piksel yang ditutupi oleh topeng berasal dari latar belakang, setel piksel ke latar belakang.

dilation: Untuk setiap piksel latar belakang, letakkan mask di sekitarnya, dan jika ada piksel yang ditutupi oleh mask berasal dari latar depan, setel piksel ke latar depan.

shapes = readImage(system.file('images', 'shapes.png', package='EBImage'))
logo = shapes[110:512,1:130]
display(logo)

kern = makeBrush(5, shape='diamond')
display(kern, interpolate=FALSE)

logo_erode= erode(logo, kern)
logo_dilate = dilate(logo, kern)

display(combine(logo_erode, logo_dilate), all=TRUE)

Pembukaan dan penutupan adalah kombinasi dari dua operasi di atas: pembukaan melakukan erosi diikuti oleh dilatasi, sedangkan penutupan melakukan sebaliknya, yaitu melakukan dilasi diikuti oleh erosi. Pembukaan berguna untuk menghilangkan derau morfologis, karena menghilangkan objek kecil dari latar belakang, dan menutup dapat digunakan untuk mengisi lubang kecil di latar depan. Operasi ini dilaksanakan oleh openingdan closing.

7 Tresholding

7.1 Thresholding

Di bagian “Memanipulasi gambar”, kami telah mendemonstrasikan cara menyetel ambang batas global pada gambar. Di sana kami menggunakan nilai batas yang berubah-ubah. Untuk gambar yang distribusi intensitas pikselnya mengikuti histogram bi-modal, pendekatan yang lebih sistematis melibatkan penggunaan metode Otsu . Metode Otsu merupakan teknik untuk melakukan thresholding citra berbasis clustering secara otomatis. Dengan asumsi distribusi intensitas bi-modal, algoritme memisahkan piksel gambar menjadi latar depan dan latar belakang. Nilai ambang optimal ditentukan dengan meminimalkan varian intra-kelas gabungan.

Ambang batas Otsu dapat dihitung menggunakan fungsi tersebut otsu. Saat dipanggil pada gambar multi-bingkai, ambang dihitung untuk setiap bingkai secara terpisah sehingga menghasilkan vektor keluaran dengan panjang yang sama dengan jumlah total bingkai pada gambar.

threshold = otsu(nuc)
threshold
## [1] 0.3535156 0.4082031 0.3808594 0.4121094
nuc_th = combine( mapply(function(frame, th) frame > th, getFrames(nuc), threshold, SIMPLIFY=FALSE) )
display(nuc_th, all=TRUE)

Perhatikan penggunaan getFramesuntuk membagi gambar menjadi daftar bingkai individu, dan combineuntuk menggabungkan kembali hasilnya.

7.2 Tresholding adaptif

Ide dari thresholding adaptif adalah bahwa, dibandingkan dengan thresholding langsung dari bagian sebelumnya, threshold diperbolehkan untuk berbeda di berbagai wilayah gambar. Dengan cara ini, seseorang dapat mengantisipasi ketergantungan spasial dari sinyal latar belakang yang mendasarinya yang disebabkan, misalnya, oleh iluminasi yang tidak merata atau oleh sinyal yang menyimpang dari objek terang di dekatnya.

Thresholding adaptif bekerja dengan membandingkan intensitas setiap piksel dengan latar belakang yang ditentukan dari lingkungan lokal. Ini dapat dicapai dengan membandingkan gambar dengan versi yang dihaluskan, di mana jendela pemfilteran lebih besar dari ukuran umum objek yang ingin kita tangkap.

disc = makeBrush(31, "disc")
disc = disc / sum(disc)
offset = 0.05
nuc_bg = filter2( nuc, disc )
nuc_th = nuc > nuc_bg + offset
display(nuc_th, all=TRUE)

Teknik ini mengasumsikan bahwa objek tersebar relatif jarang dalam citra, sehingga distribusi sinyal di lingkungan tersebut didominasi oleh latar belakang. Sementara untuk inti dalam gambar kami, asumsi ini masuk akal, untuk situasi lain Anda mungkin perlu membuat asumsi yang berbeda. Tersedia thresholding adaptif menggunakan filter linier dengan kotak persegi panjang thresh, yang menggunakan implementasi yang lebih cepat dibandingkan dengan langsung menggunakan filter2.

display( thresh(nuc, w=15, h=15, offset=0.05), all=TRUE )

8 Transformasi Fourier

8.1 Fungsi Pergeseran FFT

# FFT SHIFT
#' This function is useful for visualizing the Fourier transform with the zero-frequency 
#' component in the middle of the spectrum.
#' 
#' @param img_ff A Fourier transform of a 1D signal, 2D image, or 3D volume.
#' @param dim Number of dimensions (-1, 1, 2, 3).
#' @return A properly shifted FT of the array.
#' 
fftshift <- function(img_ff, dim = -1) {

  rows <- dim(img_ff)[1]    
  cols <- dim(img_ff)[2]
  # planes <- dim(img_ff)[3]

  swap_up_down <- function(img_ff) {
    rows_half <- ceiling(rows/2)
    return(rbind(img_ff[((rows_half+1):rows), (1:cols)], img_ff[(1:rows_half), (1:cols)]))
  }

  swap_left_right <- function(img_ff) {
    cols_half <- ceiling(cols/2)
    return(cbind(img_ff[1:rows, ((cols_half+1):cols)], img_ff[1:rows, 1:cols_half]))
  }
  
  #swap_side2side <- function(img_ff) {
  #  planes_half <- ceiling(planes/2)
  #  return(cbind(img_ff[1:rows, 1:cols, ((planes_half+1):planes)], img_ff[1:rows, 1:cols, 1:planes_half]))
  #}

  if (dim == -1) {
    img_ff <- swap_up_down(img_ff)
    return(swap_left_right(img_ff))
  }
  else if (dim == 1) {
    return(swap_up_down(img_ff))
  }
  else if (dim == 2) {
    return(swap_left_right(img_ff))
  }
  else if (dim == 3) {
    # Use the `abind` package to bind along any dimension a pair of multi-dimensional arrays
    # install.packages("abind")
    library(abind)
    
    planes <- dim(img_ff)[3]
    rows_half <- ceiling(rows/2)
    cols_half <- ceiling(cols/2)
    planes_half <- ceiling(planes/2)
    
    img_ff <- abind(img_ff[((rows_half+1):rows), (1:cols), (1:planes)], 
                    img_ff[(1:rows_half), (1:cols), (1:planes)], along=1)
    img_ff <- abind(img_ff[1:rows, ((cols_half+1):cols), (1:planes)], 
                    img_ff[1:rows, 1:cols_half, (1:planes)], along=2)
    img_ff <- abind(img_ff[1:rows, 1:cols, ((planes_half+1):planes)], 
                    img_ff[1:rows, 1:cols, 1:planes_half], along=3)
    return(img_ff)
  }
  else {
    stop("Invalid dimension parameter")
  }
}
str(img)
## Formal class 'Image' [package "EBImage"] with 2 slots
##   ..@ .Data    : num [1:768, 1:512] 0.447 0.451 0.463 0.455 0.463 ...
##   ..@ colormode: int 0
imgMat <- matrix(img, nrow = dim(img)[1], ncol = dim(img)[2])
dim(imgMat)
## [1] 768 512

8.2 FT

ft_imgMat <- fft(imgMat)  # fftw2d # Display Re(FT):
display(fftshift(ft_imgMat))
## Warning in storage.mode(x) <- "double": imaginary parts discarded in coercion

8.3 Besaran dan Fase

mag_ft_imgMat <- sqrt(Re(ft_imgMat)^2+Im(ft_imgMat)^2)
display(mag_ft_imgMat)

# Phase  <- atan(Im(img_ff)/Re(img_ff))
phase_ft_imgMat  <- atan2(Im(ft_imgMat), Re(ft_imgMat))
# display(phase_ft_imgMat)

# Display FT
EBImage::display(log(fftshift(ft_imgMat)), title="FT Magnitude (Cyrillic Alphabet)")
## Warning in storage.mode(x) <- "double": imaginary parts discarded in coercion

8.4 Rekonstruktif IFT

# IFT Reconstruction: Magnitude=mag_ft_imgMat   AND  Phase=phase_ft_imgMat
Real = mag_ft_imgMat * cos(phase_ft_imgMat)
Imaginary = mag_ft_imgMat * sin(phase_ft_imgMat)
ift_ft_imgMat = Re(fft(Real+1i*Imaginary, inverse = T)/length(mag_ft_imgMat))
EBImage::display(ift_ft_imgMat, method = "raster", title="(IFT o FT) Magnitude=Img | Phase=Img")

8.5 Pemfilteran frekuensi low-pass

mag_ft_imgMat_1 <- mag_ft_imgMat
for (i in 1:dim(mag_ft_imgMat)[1]) {
  for (j in 1:dim(mag_ft_imgMat)[2]) {
    if (abs(i-(dim(mag_ft_imgMat)[1])/2) > (dim(mag_ft_imgMat)[1])/4 | 
        abs(j-(dim(mag_ft_imgMat)[2])/2) > (dim(mag_ft_imgMat)[2])/4) {
      mag_ft_imgMat_1[i,j] <- 0
    }
  }
}

EBImage::display(mag_ft_imgMat_1, method = "raster", title="(IFT o LowPassFilter o FT) Magnitude=Img | Phase=Img")

Real = mag_ft_imgMat_1 * cos(phase_ft_imgMat)
Imaginary = mag_ft_imgMat_1 * sin(phase_ft_imgMat)
ift_ft_imgMat_1 = Re(fft(Real+1i*Imaginary, inverse = T)/length(mag_ft_imgMat_1))
EBImage::display(100*ift_ft_imgMat_1, method = "raster", title="(IFT o LowPassFilter o FT) Magnitude=Img | Phase=Img")

8.6 Penyaringan frekuensi high-pass

# High-pass frequency filtering, remove Fourier coeff's indexed below k_{x,max/4} and k_{y,max/4}
mag_ft_imgMat_2 <- mag_ft_imgMat
for (i in 1:dim(mag_ft_imgMat)[1]) {
  for (j in 1:dim(mag_ft_imgMat)[2]) {
    if (abs(i-(dim(mag_ft_imgMat)[1])/2) < (dim(mag_ft_imgMat)[1])/4 & 
        abs(j-(dim(mag_ft_imgMat)[2])/2) < (dim(mag_ft_imgMat)[2])/4) {
      mag_ft_imgMat_2[i,j] <- 0
    }
  }
}

EBImage::display(mag_ft_imgMat_2, method = "raster", title="(IFT o HighPassFilter o FT) Magnitude=Img | Phase=Img")

Real = mag_ft_imgMat_2 * cos(phase_ft_imgMat)
Imaginary = mag_ft_imgMat_2 * sin(phase_ft_imgMat)
ift_ft_imgMat_2 = Re(fft(Real+1i*Imaginary, inverse = T)/length(mag_ft_imgMat_2))
EBImage::display(ift_ft_imgMat_2, method = "raster", title="(IFT o HighPassFilter o FT) Magnitude=Img | Phase=Img")

9 Segmentasi Gambar

Segmentasi gambar melakukan partisi gambar, dan biasanya digunakan untuk mengidentifikasi objek dalam gambar. Objek terhubung yang tidak menyentuh dapat disegmentasi menggunakan fungsi tersebut bwlabel, sedangkan watersheddan propagatemenggunakan algoritme yang lebih canggih dapat memisahkan objek yang saling bersentuhan.

bwlabelmenemukan setiap kumpulan piksel yang terhubung selain latar belakang, dan memberi label ulang kumpulan ini dengan bilangan bulat unik yang meningkat. Itu dapat dipanggil pada gambar biner ambang batas untuk mengekstrak objek.

logo_label = bwlabel(logo)
table(logo_label)
## logo_label
##     0     1     2     3     4     5     6     7 
## 42217  1375  2012   934  1957  1135  1697  1063

Nilai piksel logo_labelrentang gambar dari 0 sesuai dengan latar belakang hingga jumlah objek yang dikandungnya, yang diberikan oleh

max(logo_label)
## [1] 7

Untuk menampilkan gambar kita menormalkannya ke kisaran (0,1) yang diharapkan oleh fungsi tampilan. Ini menghasilkan objek berbeda yang dirender dengan bayangan abu-abu yang berbeda.

display( normalize(logo_label) )

Gradien skala abu-abu horizontal yang dapat diamati mencerminkan cara bwlabelmemindai gambar dan memberi label pada set yang terhubung: dari kiri ke kanan dan dari atas ke bawah. Cara lain untuk memvisualisasikan segmentasi adalah dengan menggunakan colorLabelsfungsi, yang memberi kode warna pada objek dengan permutasi acak warna unik.

display( colorLabels(logo_label) )

9.1 Watershed

Beberapa nuklei nucberada cukup dekat satu sama lain dan bergabung menjadi satu objek besar saat diberi ambang batas, seperti yang terlihat di nuc_th. bwlabelakan salah mengidentifikasi mereka sebagai satu objek. Transformasi daerah aliran sungai memungkinkan untuk mengatasi masalah ini. The watershedalgoritma memperlakukan citra skala abu-abu sebagai bantuan topografi, atau tinggi-peta. Objek yang menonjol dari latar belakang diidentifikasi dan dipisahkan dengan membanjiri gambar sumber yang terbalik. Dalam kasus gambar biner, peta jaraknya dapat berfungsi sebagai peta ketinggian masukan. Peta jarak, yang berisi untuk setiap piksel jarak ke piksel latar belakang terdekat, dapat diperoleh distmap.

nmask = watershed( distmap(nuc_th), 2 )
display(colorLabels(nmask), all=TRUE)

9.2 Voronoi tessellation

Voronoi tessellation berguna ketika kita memiliki sekumpulan titik benih (atau daerah) dan ingin membagi ruang yang terletak di antara benih-benih ini sedemikian rupa sehingga setiap titik dalam ruang tersebut ditetapkan ke benih terdekatnya. Fungsi ini diimplementasikan dalam paket R (“EBImage”) oleh fungsi tersebut propagate. Mari kita ilustrasikan konsep Voronoi tessellation pada contoh dasar. Kami menggunakan topeng inti nmasksebagai benih dan membagi ruang di antara mereka.

Aplikasi web 2D Interactive Voronoi Tessellation memberikan contoh langsung dari 2D Voronoi tessellation dinamis.

voronoiExamp = propagate(seeds = nmask, x = nmask, lambda = 100)
voronoiPaint = colorLabels (voronoiExamp)
display(voronoiPaint)
## Only the first frame of the image stack is displayed.
## To display all frames use 'all = TRUE'.

10 Manipulasi Objek

10.1 Penghapusan Objek

Paket R (“EBImage”) mendefinisikan topeng objek sebagai sekumpulan piksel dengan nilai integer unik yang sama. Biasanya, gambar yang mengandung masker objek adalah hasil dari fungsi segmentasi seperti bwalabel, watershed, atau propagate. Objek dapat dihapus dari gambar tersebut dengan rmObject, yang menghapus objek dari topeng hanya dengan mengatur nilai pikselnya ke 0. Secara default, setelah penghapusan objek semua objek yang tersisa diberi label ulang sehingga ID objek tertinggi sesuai dengan jumlah objek di topeng. The reenumerateargumen dapat digunakan untuk mengubah perilaku ini dan untuk melestarikan ID objek asli.

objects = list(
    seq.int(from = 2, to = max(logo_label), by = 2),
    seq.int(from = 1, to = max(logo_label), by = 2)
    )
logos = combine(logo_label, logo_label)
z = rmObjects(logos, objects, reenumerate=FALSE)
display(z, all=TRUE)

Dalam contoh di atas, kami mendemonstrasikan bagaimana fungsi penghapusan objek dapat diterapkan ke gambar multi-bingkai dengan memberikan daftar indeks objek yang akan dihapus dari setiap bingkai. Selain itu kami telah mengatur reenumerateuntuk FALSEmenyimpan ID objek asli.

showIds = function(image) lapply(getFrames(image), function(frame) unique(as.vector(frame)))

showIds(z)
## [[1]]
## [1] 0 1 3 5 7
## 
## [[2]]
## [1] 0 2 4 6

Ingatlah bahwa 0 berarti latar belakang. Jika pada tahap tertentu kita memutuskan untuk melabel ulang objek, kita dapat menggunakan fungsi standalone untuk ini reenumarate

showIds( reenumerate(z) )
## [[1]]
## [1] 0 1 2 3 4
## 
## [[2]]
## [1] 0 1 2 3

10.2 Mengisi lubang dan daerah

filled_logo = fillHull(logo)
display(filled_logo)

floodFillmengisi wilayah gambar dengan warna tertentu. Pengisian dimulai pada titik tertentu, dan wilayah pengisian diperluas ke area yang terhubung di mana perbedaan mutlak dalam intensitas piksel tetap di bawah tolerance. Spesifikasi warna menggunakan nama warna R untuk Colorgambar, dan nilai numerik untuk gray-scalegambar.

rgblogo = toRGB(logo)
points = rbind(c(50, 50), c(100, 50), c(150, 50))
colors = c("red", "green", "blue")
rgblogo = floodFill(rgblogo, points, colors)
display( rgblogo )

display(floodFill(img, rbind(c(200, 300), c(444, 222)), col=0.2, tolerance=0.2))

10.3 Menyoroti Objek

Diberikan gambar yang berisi objek masker, fungsi tersebut paintObjectsdapat digunakan untuk menyorot objek dari topeng dalam gambar target yang disediakan dalam tgtargumen. Objek dapat diberi kerangka dan diisi dengan warna opasitas tertentu yang ditentukan dalam argumen coldan opac, masing-masing. Jika spesifikasi warna hilang atau sama, NAitu tidak dicat.

d1 = dim(img)[1:2]
overlay = Image(dim=d1)
d2 = dim(logo_label)-1

offset = (d1-d2) %/% 2

overlay[offset[1]:(offset[1]+d2[1]), offset[2]:(offset[2]+d2[2])] = logo_label

img_logo = paintObjects(overlay, toRGB(img), col=c("red", "yellow"), opac=c(1, 0.3), thick=TRUE)

display( img_logo )

Pada contoh di atas kita telah membuat mask baru yang overlaycocok dengan ukuran gambar target kita img, dan menyalin mask yang berisi logo “EBImage” ke dalam overlay mask. Output dari paintObjectsmempertahankan mode warna dari gambar targetnya, oleh karena itu untuk memiliki logo yang disorot dalam warna perlu dikonversi imgke gambar RGB terlebih dahulu, jika tidak, hasilnya akan menjadi gambar skala abu-abu. The thickArgumen kontrol objek kontur gambar: jika set ke FALSE, hanya dalam satu-pixel batas objek ditandai; jika disetel ke TRUE, batas luar juga akan disorot sehingga menghasilkan lebar kontur dua piksel yang bertambah.

#Contoh Segmentasi sel Kami menyimpulkan vignette kami dengan menerapkan fungsi yang dijelaskan sebelumnya untuk tugas segmentasi sel. Tujuan kami adalah untuk mengidentifikasi secara komputasi dan mengkarakterisasi secara kualitatif sel dalam sampel gambar mikroskop fluoresen. Meskipun ini dengan sendirinya mungkin tampak sebagai tujuan yang sederhana, pendekatan ini dapat diterapkan pada koleksi yang berisi ribuan gambar, yang tidak perlu lagi menjadi tujuan yang sederhana!

Kami mulai dengan memuat gambar inti dan badan sel. Untuk memvisualisasikan sel, kami melapisi gambar-gambar ini sebagai saluran hijau dan biru dari gambar berwarna palsu.

nuc = readImage(system.file('images', 'nuclei.tif', package='EBImage'))
cel = readImage(system.file('images', 'cells.tif', package='EBImage'))

cells = rgbImage(green=1.5*cel, blue=nuc)
display(cells, all = TRUE)

Pertama, kita segmen inti menggunakan thresh, fillHull, bwlabel dan opening.

nmask = thresh(nuc, w=10, h=10, offset=0.05)
nmask = opening(nmask, makeBrush(5, shape='disc'))
nmask = fillHull(nmask)
nmask = bwlabel(nmask)

display(nmask, all=TRUE)

Selanjutnya, kami menggunakan inti tersegmentasi sebagai benih dalam segmentasi Voronoi dari sitoplasma.

ctmask = opening(cel>0.1, makeBrush(5, shape='disc'))
cmask = propagate(cel, seeds=nmask, mask=ctmask)

display(ctmask, all=TRUE)

Untuk memvisualisasikan segmentasi kita pada yang kita gunakan paintObject.

segmented = paintObjects(cmask, cells, col='#ff00ff')
segmented = paintObjects(nmask, segmented, col='#ffff00')

display(segmented, all=TRUE)